/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package logging

import (
	"errors"
	"io"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/natefinch/lumberjack"
	"github.com/sirupsen/logrus"
	"hundsun.com/hsl/hschain/common/config"
)

const (
	configKeyTimeFormat = "log.timeFormat"
	configKeyInternalIP = "console.internalIP"
	//ConfigKeyConsoleLogLevel console log level
	ConfigKeyConsoleLogLevel = "console.logLevel"
	//ConfigKeyRedisLogLevel redis log level
	ConfigKeyRedisLogLevel = "redis.logLevel"
	//ConfigKeyEtcdLogLevel etcd log level
	ConfigKeyEtcdLogLevel = "etcd.logLevel"
	//ConfigKeyElasticSearchLogLevel es log level
	ConfigKeyElasticSearchLogLevel = "elasticSearch.logLevel"
)

const (
	configKeyLogLevel           = "log.level"
	configKeyEnableReportCaller = "log.enableReportCaller"
	configKeyEnableConsoleLog   = "log.enableConsoleLog"
	configKeyEnableFileLog      = "log.enableFileLog"
	configKeyLogFile            = "log.logFile"
	configKeyMaxFileSize        = "log.maxFileSize"
	configKeyMaxBackups         = "log.maxBackups"
	configKeyMaxAge             = "log.maxAge"
	configKeyLocalTime          = "log.localTime"
	configKeyCompress           = "log.compress"
	//configKeyEnableRemoteLog    = "log.enableRemoteLog"
	//configKeyNetwork            = "log.network"
	//configKeyAddresse           = "log.addresse"
)

var (
	once       sync.Once
	defaultLog Log
	logMutex   sync.Mutex
	baseLog    = logrus.New()
)

func init() {
	// 自动载入一个默认log,主要是为单元测试等使用
	SetDefaultLog(newloggerImpl(baseLog, map[string]interface{}{logFieldNode: ""}))
}

//SetDefaultLog set default log impl
func SetDefaultLog(dlog Log) {
	if dlog == nil {
		return
	}
	logMutex.Lock()
	defer logMutex.Unlock()
	defaultLog = dlog
}

//Init log init
func Init() {
	once.Do(func() {
		levelStr := config.GetStringConfigValue(configKeyLogLevel, "debug")
		enableReportCaller := config.GetBoolConfigValue(configKeyEnableReportCaller, false)
		enableConsoleLog := config.GetBoolConfigValue(configKeyEnableConsoleLog, true)
		enableFileLog := config.GetBoolConfigValue(configKeyEnableFileLog, true)
		logFile := config.GetStringConfigValue(configKeyLogFile, "logs/hschain.log")
		maxFileSize := config.GetIntConfigValue(configKeyMaxFileSize, 300)
		maxBackups := config.GetIntConfigValue(configKeyMaxBackups, 100)
		maxAge := config.GetIntConfigValue(configKeyMaxAge, 28)
		localTime := config.GetBoolConfigValue(configKeyLocalTime, true)
		compress := config.GetBoolConfigValue(configKeyCompress, true)
		//enableRemoteLog := config.GetBoolConfigValue(configKeyEnableRemoteLog, true)
		//network := config.GetStringConfigValue(configKeyNetwork, "udp")
		//address := config.GetStringConfigValue(configKeyAddresse, "127.0.0.1:18080")
		nodeIP := config.GetStringConfigValue(configKeyInternalIP, "")
		defaultTimeFormat := config.GetStringConfigValue(configKeyTimeFormat, "2006-01-02 15:04:05.000")

		defaultLogLevel, err := ParseLevel(levelStr)
		if err != nil {
			defaultLogLevel = Info
		}

		logLevel, _ := logrus.ParseLevel(defaultLogLevel.String())

		log.Println("default log level:", levelStr)

		// 设置日志等级
		baseLog.SetLevel(logLevel)
		// 设置日志格式
		baseLog.SetFormatter(&logrus.JSONFormatter{TimestampFormat: defaultTimeFormat})
		// 设置打印调用源文件、行号、方法
		if enableReportCaller {
			baseLog.SetReportCaller(enableReportCaller)
		}
		var writes []io.Writer
		if enableConsoleLog {
			writes = append(writes, os.Stdout)
		}
		if enableFileLog {
			lumberjackLogrotate := &lumberjack.Logger{
				Filename:   logFile,
				MaxSize:    maxFileSize,
				MaxBackups: maxBackups,
				MaxAge:     maxAge,
				LocalTime:  localTime,
				Compress:   compress,
			}
			writes = append(writes, lumberjackLogrotate)
		}
		// 设置output
		if len(writes) > 0 {
			logMultiWriter := io.MultiWriter(writes...)
			baseLog.SetOutput(logMultiWriter)
		} else {
			baseLog.SetOutput(os.Stderr)
		}
		// 设置输出远程日志
		/*if enableRemoteLog {
			go func() { // 重试直到连接成功
				for {
					hook, err := slhooks.NewSyslogHook(network, address, syslog.LOG_INFO, "")
					if err != nil {
						continue
						time.Sleep(time.Second * 10)
					}
					baseLog.AddHook(hook)
					log.Println("add sys net hook success ", address)
					break
				}
			}()
		}*/
		SetDefaultLog(newloggerImpl(baseLog, map[string]interface{}{logFieldNode: nodeIP}))
	})
}

// GetDefaultLog 获取默认日志对象，使用全局配置的默认日志级别
func GetDefaultLog() Log {
	return defaultLog
}

//LogLevel 日志级别
type LogLevel uint8

const (
	_ LogLevel = iota
	//Fatal fatal level
	Fatal
	//Error error level
	Error
	//Warn warn level
	Warn
	//Info info level
	Info
	//Debug debug level
	Debug
	//Trace trace level
	Trace
)

//String loglevel to string
func (l LogLevel) String() string {
	switch l {
	case Fatal:
		return "fatal"
	case Error:
		return "error"
	case Warn:
		return "warn"
	case Info:
		return "info"
	case Debug:
		return "debug"
	case Trace:
		return "trace"
	default:
		return "unknown"
	}
}

// ParseLevel 解析日志级别
func ParseLevel(levelStr string) (LogLevel, error) {
	switch strings.ToLower(levelStr) {
	case "fatal":
		return Fatal, nil
	case "error":
		return Error, nil
	case "warn", "warning":
		return Warn, nil
	case "info":
		return Info, nil
	case "debug":
		return Debug, nil
	case "trace":
		return Trace, nil
	default:
		return 0, errors.New("unknown log level " + levelStr)
	}
}

//GetLogWithCommonConf （模块管理服务）依据common_conf配置项，获取对应日志实例
func GetLogWithCommonConf(configKey, fieldName string, fieldValue interface{}) Log {
	// 不再新建log句柄，只进行派生
	return GetDefaultLog().WithField(fieldName, fieldValue)
}

// NewLog 指定日志级别
func NewLog(level LogLevel, fieldName string, fieldValue interface{}) Log {
	// 不再新建log句柄，只进行派生
	return GetDefaultLog().WithField(fieldName, fieldValue)
}

// Log 日志接口
type Log interface {
	//Fatal ...
	Fatal(args ...interface{})
	//Fatalf ...
	Fatalf(format string, args ...interface{})
	//Error ...
	Error(args ...interface{})
	//Errorf ...
	Errorf(format string, args ...interface{})
	//Warn ...
	Warn(args ...interface{})
	//Warnf ...
	Warnf(format string, args ...interface{})
	//Info ...
	Info(args ...interface{})
	//Infof ...
	Infof(format string, args ...interface{})
	//Debug ...
	Debug(args ...interface{})
	//Debugf ...
	Debugf(format string, args ...interface{})
	//Trace ...
	Trace(args ...interface{})
	//Tracef ...
	Tracef(format string, args ...interface{})

	// WithField 从当前Log派生出一个新的日志并增加一个字段
	WithField(fieldName string, fieldValue interface{}) Log

	// WithFields 从当前Log派生出一个新的日志并增加多个字段
	WithFields(fields map[string]interface{}) Log

	// WithElapsedTimeField 从当前Log派生出一个增加了耗时（毫秒）字段的新Log
	WithCostField(start time.Time) Log

	//Printf ...
	Printf(format string, v ...interface{})

	//IsDebugEnabled 是否开启了debug及更高等级的日志
	IsDebugEnabled() bool

	//IsTraceEnabled 是否开启了trace级别日志
	IsTraceEnabled() bool

	// ElapsedTime 打印函数用时（毫秒）
	// 用法: defer ElapsedTime("methodName", time.Now())
	ElapsedTime(methodName string, start time.Time)

	//GetLevel get present log level
	GetLevel() LogLevel
}
